package com.keyingbo.websocket.server;

import com.keyingbo.websocket.utils.TokenUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;


/**
 * 保持一个浏览器登录 服务端代码
 * @ServerEndpoint
 * 注解是一个类层次的注解，它的功能主要是将目前的类定义成一个websocket服务器端,
 * 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
 */
@ServerEndpoint("/websocketKeepOneLogin/{token}")
@Component
public class WebSocketKeepOneLoginServer {
    static Logger logger = LoggerFactory.getLogger(WebSocketKeepOneLoginServer.class);
    //静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。
    private static final AtomicInteger OnlineCount = new AtomicInteger(0);
    //concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话，可以使用Map来存放，其中Key可以为用户标识
    private static ConcurrentHashMap<String, List<WebSocketKeepOneLoginServer>> webSocketMap = new ConcurrentHashMap<String, List<WebSocketKeepOneLoginServer>>();
    //与某个客户端的连接会话，需要通过它来给客户端发送数据
    private Session WebSocketsession;
    //当前发消息的人员userId
    private String userId = "";

    /**
     * 连接建立成功调用的方法*/
    @OnOpen
    public void onOpen(@PathParam(value = "token") String param, Session WebSocketsession, EndpointConfig config) {
        userId = TokenUtil.getUserId(param);
        this.WebSocketsession = WebSocketsession;
        addWebSocket(userId , this);
        logger.info("当前存在人数有：" + OnlineCount.get());
    }

    //增加socket,一个用户也可能连接了多端
    private void addWebSocket(String userId , WebSocketKeepOneLoginServer webSocketKeepOneLoginServer) {
        synchronized (WebSocketKeepOneLoginServer.class) {
            List<WebSocketKeepOneLoginServer> webSocketKeepOneLoginServerList = webSocketMap.get(userId);
            if (CollectionUtils.isEmpty(webSocketKeepOneLoginServerList)) {
                webSocketKeepOneLoginServerList = new ArrayList<>();
                webSocketKeepOneLoginServerList.add(webSocketKeepOneLoginServer);
                webSocketMap.put(userId , webSocketKeepOneLoginServerList);
                OnlineCount.incrementAndGet(); // 在线数加1
            } else {
                webSocketKeepOneLoginServerList.add(webSocketKeepOneLoginServer);
            }
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session WebSocketsession) {
        if (!StringUtils.isEmpty(userId)){
            removeOneSocket(userId , WebSocketsession);
            logger.info("有连接关闭，当前连接数为：{}", OnlineCount.get());
        }
    }

    /**
     * 移出一个socket
     * @param userId
     * @param webSocketsession
     */
    private void removeOneSocket(String userId, Session webSocketsession) {
        synchronized (WebSocketKeepOneLoginServer.class) {
            List<WebSocketKeepOneLoginServer> webSocketKeepOneLoginServerList = webSocketMap.get(userId);
            if (!CollectionUtils.isEmpty(webSocketKeepOneLoginServerList)) {
                webSocketKeepOneLoginServerList.remove(webSocketsession);
            }
            //处理完后如果列表为空，则清除登录信息
            if (!CollectionUtils.isEmpty(webSocketKeepOneLoginServerList)) {
                webSocketMap.remove(userId);
                int cnt = OnlineCount.decrementAndGet();
                logger.info("有连接关闭，当前连接数为：{}", cnt);
            }
        }
        webSocketMap.remove(userId , WebSocketsession);//从set中删除
    }

    /**
     * 收到客户端消息后调用的方法（每隔一段时间收到客户端的请求）
     * @param message 客户端发送过来的消息*/
    @OnMessage
    public void onMessage(String message, Session session) {
        String userId = TokenUtil.getUserId(message);//通过token获取当前的用户ID
        String token = TokenUtil.getToken(userId);//再获取此时生效的token
        sendToken(session , token);
    }

    /**
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        logger.error("发生错误：{}，Session ID： {}",error.getMessage(),session.getId());
        error.printStackTrace();
    }

    /**
     * 通知其他已登录端退出（如果有）
     * @param userId
     * @throws IOException
     */
    public void noticeToOtherWeb(String userId) {
        List<WebSocketKeepOneLoginServer> webSocketServerList = webSocketMap.get(userId);
        if (webSocketServerList == null) return;

        for (WebSocketKeepOneLoginServer webSocketServer : webSocketServerList) {
            if ( webSocketServer != null && webSocketServer.WebSocketsession.isOpen()){
                sendToken(webSocketServer.WebSocketsession,TokenUtil.getToken(userId));
            }
        }
    }

    private void sendToken (Session session , String token) {
        try {
            session.getBasicRemote().sendText(token);
        } catch (IOException e) {
            logger.warn("发送失败");
        }
    }
}
